package redis.client.nedis.commands;

import io.netty.buffer.ByteBuf;
import io.netty.util.CharsetUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import redis.client.nedis.util.Encoding;

/**
 * redis命令
 *
 * @author gaodong
 */
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class RedisCommand {
    public static final byte[] ARGS_PREFIX = "*".getBytes();
    public static final byte[] CRLF = "\r\n".getBytes();
    public static final byte[] BYTES_PREFIX = "$".getBytes();
    public static final byte[] EMPTY_BYTES = new byte[0];

    /**
     * 命令
     */
    private Object[] cmds;

    /**
     * 命令类型，是否为master命令。这里
     * master命令指写命令，如set,hset,sadd,zadd等
     * slave命令指读命令，如get,hget,scard,zcard等
     */
    private CommandType commandType;

    /**
     * 在集群模式下启用，key对应的节点
     */
    private int slot;

    /**
     * 根据命令生成Command
     *
     * @param paras
     * @return
     */
    public static RedisCommand getInstance(Object... paras) {
        CommandType ct;
        switch ((String) paras[0]) {
            case ServerCommands.GET:
            case ServerCommands.AUTH:
                ct = CommandType.SLAVE;
            default:
                ct = CommandType.MASTER;
        }
        return new RedisCommand(paras, ct, 0);
    }

    public void write(ByteBuf os) {
        int length = cmds.length;
        os.writeBytes(ARGS_PREFIX);
        os.writeBytes(Encoding.numToBytes(length, true));
        for (Object object : cmds) {
            writeObject(os, object);
        }
    }

    private static void writeObject(ByteBuf os, Object object) {

        if (object == null) {
            writeArgument(os, EMPTY_BYTES);
            return;
        }

        if (object instanceof String) {
            writeArgument(os, ((String) object).getBytes(CharsetUtil.UTF_8));
            return;
        }

        if (object instanceof ByteBuf) {
            writeArgument(os, (ByteBuf) object);
            return;
        }

        if (object instanceof byte[]) {
            writeArgument(os, (byte[]) object);
            return;
        }

        writeArgument(os, object.toString().getBytes(CharsetUtil.UTF_8));
    }

    private static void writeArgument(ByteBuf os, byte[] argument) {
        os.writeBytes(BYTES_PREFIX);
        os.writeBytes(Encoding.numToBytes(argument.length, true));
        os.writeBytes(argument);
        os.writeBytes(CRLF);
    }

    private static void writeArgument(ByteBuf os, ByteBuf argument) {
        os.writeBytes(BYTES_PREFIX);
        os.writeBytes(Encoding.numToBytes(argument.readableBytes(), true));
        os.writeBytes(argument);
        os.writeBytes(CRLF);
    }

    private byte[] getBytes(Object object) {
        byte[] argument;
        if (object == null) {
            argument = EMPTY_BYTES;
        } else if (object instanceof byte[]) {
            argument = (byte[]) object;
        } else if (object instanceof ByteBuf) {
            argument = ((ByteBuf) object).array();
        } else if (object instanceof String) {
            argument = ((String) object).getBytes(CharsetUtil.UTF_8);
        } else {
            argument = object.toString().getBytes(CharsetUtil.UTF_8);
        }
        return argument;
    }

    public byte[] getName() {
        return getBytes(cmds[0]);
    }
}
